package org.myminidao;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.myminidao.annotation.Column;
import org.myminidao.annotation.MyMiniDao;
import org.myminidao.annotation.MyParam;
import org.myminidao.annotation.Sql;
import org.myminidao.annotation.SqlBean;
import org.myminidao.conf.pojo.DaoPojo;
import org.myminidao.conf.pojo.MyDaoConfiguration;
import org.myminidao.conf.pojo.ParamPojo;
import org.myminidao.conf.pojo.SqlMapPojo;
import org.myminidao.conf.pojo.SqlPojo;
import org.myminidao.enums.MyJDBCType;
import org.myminidao.session.DefaultSession;
import org.myminidao.session.MyMiniDaoSession;
import org.myminidao.utils.PackageUtil;
import org.myminidao.utils.XStreamUtil;

public class MyDaoFactory {
	private static File configFile;
	
	private static MyDaoConfiguration config;
	
	private static Properties databaseProperties;
	
	private static DataSource dataSource;
	
	private static Map<String, DaoPojo> daoMap;
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void getConfig(String filePath) {
		filePath = ClassLoader.getSystemResource("").getPath() + filePath;
		configFile = new File(filePath);
		config = (MyDaoConfiguration)XStreamUtil.getConfigXStream().fromXML(configFile);
		
		databaseProperties = new Properties();
		databaseProperties.setProperty("driverClassName", config.getDriverclass());
		databaseProperties.setProperty("url", config.getUrl());
		databaseProperties.setProperty("username", config.getUsername());
		databaseProperties.setProperty("password", config.getPassword());
		databaseProperties.setProperty("initialSize", "30");
		databaseProperties.setProperty("maxTotal", "30");
		databaseProperties.setProperty("maxIdle", "10");
		databaseProperties.setProperty("minIdle", "5");
		databaseProperties.setProperty("maxWaitMillis", "1000");
		databaseProperties.setProperty("removeAbandonedOnMaintenance", "true");
		databaseProperties.setProperty("removeAbandonedOnBorrow", "true");
		databaseProperties.setProperty("removeAbandonedTimeout", "1");
		
		//����DAO������Ϣ
		String daoPackage = config.getDaopackage();
		String daoPath = ClassLoader.getSystemResource("").getPath() + daoPackage.replace(".", "\\");
		List<String> names = PackageUtil.getPackageClass(daoPath, null);
		daoMap = new HashMap<String, DaoPojo>();
		if (null != names && 0 != names.size()) {
			for (String string : names) {
				try {
					DaoPojo dao = new DaoPojo();
					Map<String, SqlPojo> map = new HashMap<String, SqlPojo>();
					Class c = Class.forName(string);
					boolean isDao = c.isAnnotationPresent(MyMiniDao.class);
					if (isDao) {
						Annotation[] annotations = c.getAnnotations();
						for (int i = 0; i < annotations.length; i++) {
							Annotation annotation = annotations[i];
							if (annotation.annotationType().equals(MyMiniDao.class)) {
								MyMiniDao miniDao = (MyMiniDao)annotation;
								dao.setPack(miniDao.pack());
							}
						}
					}
					
					//��װ��Ӧsql
					Method[] methods = c.getMethods();
					for (int i = 0; i < methods.length; i++) {
						Method method = methods[i];
						SqlPojo sqlPojo = new SqlPojo();
						if (method.isAnnotationPresent(Sql.class)) {
							Annotation[] annotations = method.getAnnotations();
							for (int j = 0; j < annotations.length; j++) {
								Annotation annotation = annotations[j];
								if (annotation.annotationType().equals(Sql.class)) {
									Sql sql = (Sql)annotation;
									String fileName = sql.value();
									String filePack = dao.getPack();
									//sql�ļ�λ��
									String sqlPath = ClassLoader.getSystemResource("").getPath() + filePack.replace(".", "\\");
									sqlPojo.setMethodFileName(fileName);
									sqlPojo.setSqlFilePath(sqlPath);
									sqlPojo.setType(sql.type());
								}
							}
							String type = method.getGenericReturnType().toString();
							Boolean flag = Boolean.FALSE;
							if (type.startsWith("java.util.List")) {
								sqlPojo.setIsList(Boolean.TRUE);
								String[] strs = type.split("<");
								type = strs[i].split(">")[0];
							} else {
								sqlPojo.setIsList(Boolean.FALSE);
							}
							if ("class java.lang.String".equals(type)) {
								sqlPojo.setReturnJdbcType(MyJDBCType.STRING);
								flag = Boolean.TRUE;
							} else if ("double".equals(type) || "class java.lang.Double".equals(type)) {
								sqlPojo.setReturnJdbcType(MyJDBCType.DOUBLE);
								flag = Boolean.TRUE;
							} else if ("int".equals(type) || "class java.lang.Integer".equals(type)) {
								sqlPojo.setReturnJdbcType(MyJDBCType.INTEGER);
								flag = Boolean.TRUE;
							} else if ("float".equals(type) || "class java.lang.Float".equals(type)) {
								sqlPojo.setReturnJdbcType(MyJDBCType.FLOAT);
								flag = Boolean.TRUE;
							} else if ("class java.util.Date".equals(type)) {
								sqlPojo.setReturnJdbcType(MyJDBCType.TIME);
								flag = Boolean.TRUE;
							}
							
							if (!flag.booleanValue()) {
								sqlPojo.setReturnClass(type);
								Class returnClass = Class.forName(type);
								if (returnClass.isAnnotationPresent(SqlBean.class)) {
									List<SqlMapPojo> sqlList = new ArrayList<SqlMapPojo>();
									Class returnClazz = Class.forName(type);
									Field[] fields = returnClazz.getDeclaredFields();
									for (Field field : fields) {
										if (field.isAnnotationPresent(Column.class)) {
											Column column = field.getAnnotation(Column.class);
											SqlMapPojo smp = new SqlMapPojo();
											smp.setColumn(column.value());
											smp.setJdbcType(column.jdbcType());
											smp.setVariableName(field.getName());
											sqlList.add(smp);
										}
									}
									sqlPojo.setSqlMapList(sqlList);
								}
							} else {
								sqlPojo.setReturnClass(null);
							}
							
							//��װ��������
							Parameter[] parameters = method.getParameters();
							if (null != parameters && 0 != parameters.length) {
								List<ParamPojo> paramList = new ArrayList<ParamPojo>();
								for (int j = 0; j < parameters.length; j++) {
									Parameter parameter = parameters[j];
									if (null != parameter.getAnnotation(MyParam.class)) {
										MyParam myParam = parameter.getAnnotation(MyParam.class);
										ParamPojo paramPojo = new ParamPojo();
										paramPojo.setField(parameter.getName());
										paramPojo.setFieldClass(parameter.getType().toString());
										paramPojo.setJdbcType(myParam.jdbcType());
										paramPojo.setName(myParam.name());
										paramList.add(paramPojo);
									}
								}
								
								sqlPojo.setParamList(paramList);
							}
							
							map.put(method.getName(), sqlPojo);
						}
					}
					
					dao.setMap(map);
					daoMap.put(string, dao);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private static DataSource getDataSource() {
		try {
			dataSource = BasicDataSourceFactory.createDataSource(databaseProperties);
			return dataSource;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static MyMiniDaoSession getSession() throws SQLException {
		DefaultSession session = new DefaultSession();
		session.setConnection(getDataSource().getConnection());
		
		return session;
	}
	
	public static void main(String[] args) {
		System.out.println(MyDaoFactory.class.getClass().getResource("/").getPath());
		getConfig(MyDaoFactory.class.getClass().getResource("/").getPath() + "config/dao.xml");
		
		DataSource ds = getDataSource();
		try {
			System.out.println(ds.getConnection());
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		
	}

	public static Map<String, DaoPojo> getDaoMap() {
		return daoMap;
	}
}
